
/**
 * Code for the Global Descriptor Table (GDT)
 *
 * 7/27/06 Pavel - Completed
 */
#include <gdt.h>


/**
 * With this we can define a GDT entry. Usage of the "packed"
 * attribute is so that the compiler doesn't "optimize" the
 * code in unwanted ways.*/
struct gdt_entry
{
  unsigned short limit_low;
  unsigned short base_low;
  unsigned char base_middle;
  unsigned char access;
  unsigned char granularity;
  unsigned char base_high;
}__attribute__((packed));

struct gdt_ptr
{
  unsigned int base;
  unsigned short limit;  //The maximum bytes the GDT takes up minus one.
}__attribute((packed));

/** Our [null, code, data] 3-entry gdt*/
struct gdt_entry gdt[3];
struct gdt_ptr gp;

/**
 * We need to reference to an external asm routine to reload
 * our segment registers.*/
extern void gdt_flush();

/*Setup a descriptor in the GDT - taken directly from another OS*/
void gdt_set_gate(int num, unsigned long base, unsigned long limit, unsigned char access, unsigned char gran)
{
  /* Setup the descriptor base address */
  gdt[num].base_low = (base & 0xFFFF);
  gdt[num].base_middle = (base >> 16) & 0xFF;
  gdt[num].base_high = (base >> 24) & 0xFF;

  /* Setup the descriptor limits */
  gdt[num].limit_low = (limit & 0xFFFF);
  gdt[num].granularity = ((limit >> 16) & 0x0F);

  /* Finally, set up the granularity and access flags */
  gdt[num].granularity |= (gran & 0xF0);
  gdt[num].access = access;
}

/**
 * And finally, this void will set up the special gp pointer,
 * setup the first three entries in the gdt [null, code, data],
 * and lastly call gdt_flush() to tell the CPU where the new GDT
 * is using lgdt, and update the segment registers. */
void gdt_install()
{
  /* Take care of the pointer and limit */
  gp.limit = (sizeof(struct gdt_entry) * 3) - 1;
  gp.base = (unsigned int) &gdt;

  /* our NULL descriptor */
  gdt_set_gate(0, 0, 0, 0, 0);

  /** 
   * Our second entry is the code segment. We'll make the base
   * adress 0, the limit 4GB, 4kb granularity, 32-bit opcodes, 
   * and a descriptor to the code segment */
  gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF);

  /**
   * The third entry is our data segment. It's exactly the same
   * as the code segment with the exception of being a descriptor
   * to the data segment, using 0x92 */
  gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xCF);

  /* Flush out the old GDT and install the new changes, and we are DONE! */
  gdt_flush();

}
